//
//  MoyaCachePlugin.swift
//  Common
//
//  Created by 我骑着蜗牛走天下 on 2025/11/13.
//

import Moya
import Foundation


/// 网络数据缓存插件

public class MoyaCachePlugin: PluginType {
    
    public init() { }
    
    // 用来保存请求开始时间和uuid，key: uuid
    private var startTimes: [String: Date] = [:]
    
    // prepare 在请求发送前调用，可以修改请求
    public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
        var request = request
        
        // 生成uuid
        let uuid = UUID().uuidString
        
        // 把uuid写入请求头
        request.addValue(uuid, forHTTPHeaderField: "X-Request-UUID")
        
        // 记录请求开始时间和uuid
        startTimes[uuid] = Date()
        
        return request
    }
    
    public func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
        switch result {
        case .success(let response):
            // 从响应请求头中取uuid
            let uuid = response.response?.allHeaderFields["X-Request-UUID"] as? String
            
            // 如果响应头没带uuid，可以从请求头获取（Moya不直接提供）
            // 这里简化处理，假设我们能拿到uuid
            guard let uuid = uuid ?? startTimes.keys.first else {
                print("⚠️ 没有找到uuid，无法缓存")
                return
            }
            
            guard let startTime = startTimes[uuid] else {
                print("⚠️ 没有找到开始时间")
                return
            }
            
            let duration = Float(Date().timeIntervalSince(startTime))
            
            // 请求信息
            let url = response.response?.url?.absoluteString ?? "unknown"
            let method = response.request?.httpMethod ?? "unknown"
            
            let headers = response.request?.allHTTPHeaderFields
            let headersString: String?
            if let headers = headers,
               let data = try? JSONSerialization.data(withJSONObject: headers, options: .prettyPrinted),
               let str = String(data: data, encoding: .utf8) {
                headersString = str
            } else {
                headersString = nil
            }
            
            // 请求体处理
            let maxRequestBodySize = 1024 * 50 // 1KB阈值
            
            let requestBody: String?
            if let bodyData = response.request?.httpBody {
                if bodyData.count <= maxRequestBodySize {
                    requestBody = String(data: bodyData, encoding: .utf8)
                } else {
                    requestBody = "Large request body size: \( Int(bodyData.count / 1024) ) KB"
                }
            } else {
                requestBody = nil
            }
            
            let responseBody = String(data: response.data, encoding: .utf8)
            
            let statusCode = response.statusCode
            
            DispatchQueue.global(qos: .userInitiated).async {
                
                let cacheItem = NetworkCacheItem(
                    appLaunchID: nil,
                    uuid: uuid,
                    url: url,
                    method: method,
                    timestamp: startTime,
                    metrics: duration,
                    headers: headersString,
                    statusCode: statusCode,
                    requestBody: requestBody?.formattedJSONString() ,
                    responseBody: responseBody?.formattedJSONString() ,
                    errorDescription: nil
                )
                
                NetworkCacheManager.shared.addCacheItem(cacheItem)
                
            }
            
            startTimes.removeValue(forKey: uuid)
            
        case .failure(let error):
            // 失败时也尝试保存缓存
            
            // 这里没法直接拿到请求和uuid，插件接口限制，可以考虑用其他方式传递uuid
            
            // 简单示例，生成一个uuid
            let uuid = UUID().uuidString
            let timestamp = Date()
            
            let cacheItem = NetworkCacheItem(
                appLaunchID: nil,
                uuid: uuid,
                url: "unknown",
                method: "unknown",
                timestamp: timestamp,
                metrics: nil,
                headers: nil,
                statusCode: nil,
                requestBody: nil,
                responseBody: nil,
                errorDescription: error.localizedDescription
            )
            
            NetworkCacheManager.shared.addCacheItem(cacheItem)
        }
    }
}



// MARK: - 缓存文件管理

public class NetworkCacheItem: Codable {
    
    /// 启动周期标识
    let appLaunchID: String?
    /// 唯一标识
    let uuid: String
    /// 完整url
    let url: String
    /// 请求方式
    let method: String
    /// 请求开始时间
    let timestamp: Date
    /// 任务耗时
    let metrics: Float?
    /// 请求头
    let headers: String?
    /// 状态码
    let statusCode: Int?
    /// 请求信息
    let requestBody: String?
    /// 响应信息
    let responseBody: String?
    /// 错误信息
    let errorDescription: String?
    
    init(appLaunchID: String?, uuid: String, url: String, method: String, timestamp: Date, metrics: Float?, headers: String?, statusCode: Int?, requestBody: String?, responseBody: String?, errorDescription: String?) {
        self.appLaunchID = appLaunchID
        self.uuid = uuid
        self.url = url
        self.method = method
        self.timestamp = timestamp
        self.metrics = metrics
        self.headers = headers
        self.statusCode = statusCode
        self.requestBody = requestBody
        self.responseBody = responseBody
        self.errorDescription = errorDescription
    }
    
    func toFormattedString() -> String {
        var lines: [String] = []
        
        lines.append("App Launch ID: \(appLaunchID ?? "nil")")
        lines.append("\nUUID: \(uuid)")
        lines.append("\nURL: \(url)")
        lines.append("\n请求方式: \(method)")
        
        // 格式化日期
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        let dateString = formatter.string(from: timestamp)
        lines.append("\n请求开始时间: \(dateString)")
        
        if let metrics = metrics {
            lines.append("\n耗时: \(metrics)")
        } else {
            lines.append("\n耗时: nil")
        }
        
        lines.append("\n请求头: \(headers ?? "nil")")
        
        if let statusCode = statusCode {
            lines.append("\n状态码: \(statusCode)")
        } else {
            lines.append("\n状态码: nil")
        }
        
        lines.append(" ------------ 请求数据 ------------ \n")
        lines.append("\n请求体: \(requestBody ?? "nil")")
        lines.append("\n返回数据: \(responseBody ?? "nil")")
        lines.append("\n错误信息: \(errorDescription ?? "nil")")
        
        return lines.joined(separator: "\n")
    }
    
    
}

public final class NetworkCacheManager: @unchecked Sendable {
    
    // 当前启动周期ID，启动时赋值
    public let appLaunchID: String
    
    static let cacheFilePrefix = "network_cache_"
    static let cacheFileExtension = "json"
    // 当前启动周期的文件名称
    private static let cacheFileName = "\(cacheFilePrefix)\(Date().toString(dateFormat: .custom("yyyy_MM_dd_HH_mm_ss"))).\(cacheFileExtension)"
    private static let cacheValidity: TimeInterval = 2 * 24 * 60 * 60 // 2天秒数
    
    
    // 当前缓存
    private var cache: [NetworkCacheItem] = []
    // 当前启动周期的文件完整路径
    private let fileURL: URL
    
    // 串行队列，保证读写操作线程安全
    private let ioQueue = DispatchQueue(label: "com.yourapp.networkCacheQueue")
    
    public static let shared = NetworkCacheManager()
    
    private init() {
        // 获取当前的 Documents 目录路径
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        fileURL = paths[0].appendingPathComponent(NetworkCacheManager.cacheFileName)
        
        // 生成启动唯一ID
        appLaunchID = UUID().uuidString
        
        // 清理过期数据
//        cleanExpiredCacheFiles()
        
    }
    
    // MARK: - 读取文件
    /// 读取文件
    public func loadFileAsync(url: URL, completion: @escaping ([NetworkCacheItem]) -> Void) {
        ioQueue.async { [weak self] in
            guard let self = self else { return }
            let items = self.loadFile(url: url)
            DispatchQueue.main.async {
                completion(items)
            }
        }
    }
    
    private func loadFile(url: URL) -> [NetworkCacheItem] {
        do {
            let data = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            return try decoder.decode([NetworkCacheItem].self, from: data)
        } catch {
            // 文件不存在或解析失败，初始化为空
            return []
        }
    }
    
    
    // MARK: - 保存缓存
    func saveCache() {
        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            encoder.dateEncodingStrategy = .iso8601
            let data = try encoder.encode(cache)
            try data.write(to: fileURL, options: .atomic)
        } catch {
            print("NetworkCacheManager saveCache error: \(error)")
        }
    }
    
    /// 添加缓存项（异步写文件）
    fileprivate func addCacheItem(_ item: NetworkCacheItem) {
        ioQueue.async { [weak self] in
            guard let self = self else { return }
            var newItem = item
            newItem = NetworkCacheItem(
                appLaunchID: self.appLaunchID,  // 赋值启动ID
                uuid: item.uuid,
                url: item.url,
                method: item.method,
                timestamp: item.timestamp,
                metrics: item.metrics,
                headers: item.headers,
                statusCode: item.statusCode,
                requestBody: item.requestBody,
                responseBody: item.responseBody,
                errorDescription: item.errorDescription
            )
            cache.append(newItem)
            saveCache()
        }
    }
    
    // MARK: - 清理过期缓存文件
    
    // 清理过期缓存文件
    public func cleanExpiredCacheFiles() {
        ioQueue.async {
            
            let fileManager = FileManager.default
            
            // 获取Documents目录
            guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
                print("❌ 找不到Documents目录")
                return
            }
            
            do {
                let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil)
                
                // 过滤出符合缓存文件名格式的文件
                let cacheFiles = fileURLs.filter { url in
                    url.lastPathComponent.hasPrefix(Self.cacheFilePrefix) &&
                    url.pathExtension == Self.cacheFileExtension
                }
                
                
                let now = Date()
                
                for fileURL in cacheFiles {
                    let fileName = fileURL.lastPathComponent // e.g. "network_cache_2025_06_28_15_30_00.json"
                    
                    // 提取日期字符串，去掉前缀和后缀
                    let startIndex = fileName.index(fileName.startIndex, offsetBy: Self.cacheFilePrefix.count)
                    let endIndex = fileName.index(fileName.endIndex, offsetBy: -(".json".count))
                    let dateString = String(fileName[startIndex..<endIndex])
                    // 日期格式，和文件名中日期格式对应
                    if let fileDate = dateString.toDate(format: "yyyy_MM_dd_HH_mm_ss") {
                        let age = now.timeIntervalSince(fileDate)
                        if age > Self.cacheValidity {
                            // 过期，删除文件
                            do {
                                try fileManager.removeItem(at: fileURL)
                                print("✅ 删除过期缓存文件：\(fileName)")
                            } catch {
                                print("❌ 删除缓存文件失败：\(fileName)，错误：\(error)")
                            }
                        }
                    } else {
                        print("❌ 文件名日期格式错误，无法解析：\(fileName)")
                    }
                }
            } catch {
                print("❌ 读取Documents目录失败：\(error)")
            }
            
        }
        
    }
    
    /// 获取当前缓存
    public func getCache() -> [NetworkCacheItem] {
        return cache
    }
    
    /// 获取缓存路径下所有文件路径数组
    public func listAllFilesInDocuments() -> [(title: String, url: URL)] {
        let fileManager = FileManager.default
        // 获取Documents目录URL
        guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
            print("❌ 找不到Documents目录")
            return []
        }
        
        do {
            // 获取Documents目录下所有文件和文件夹的URL数组
            let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil)
            
            if fileURLs.isEmpty {
                print("Documents目录为空")
            } else {
//                print("Documents目录下的文件和文件夹：")
//                for url in fileURLs {
//                    print("- \(url.lastPathComponent)")
//                }
            }
            
            // 定义前缀和后缀
            let prefix = "network_cache_"
            let suffix = ".json"
            
            // 解析文件名中的日期部分作为title
            return fileURLs.compactMap { url -> (date: Date, title: String, url: URL)? in
                let fileName = url.lastPathComponent
                
                // 判断文件名是否符合格式
                guard fileName.hasPrefix(prefix), fileName.hasSuffix(suffix) else {
                    return nil
                }
                
                // 提取中间日期字符串
                let startIndex = fileName.index(fileName.startIndex, offsetBy: prefix.count)
                let endIndex = fileName.index(fileName.endIndex, offsetBy: -suffix.count)
                var dateString = String(fileName[startIndex..<endIndex])
                
                guard let date = dateString.toDate(format: "yyyy_MM_dd_HH_mm_ss") else {
                    return nil
                }
                
                let title = date.toString(dateFormat: .custom("MM-dd HH:mm"))
                return (date: date, title: title, url: url)
            }
            .sorted(by: { $0.date > $1.date }) // 按日期降序排序，越近的时间越前面
            .map { (title: $0.title, url: $0.url) }
            
        } catch {
            print("读取Documents目录内容失败：\(error)")
            return []
        }
    }
    
    
}
